// 与数据库打交道
const {
  create,
  update,
  del,
  list,
  count,
  get,
  login,
  getByPhone,
  loginByPhone,
} = require('../model/sys_user')
const {
  Success,
  SysErr,
  AccountOrPasswordErr,
  AccountDisabledErr,
  AccountExistsErr,
  AccountNotExistsErr,
} = require('../response/code')

const { createToken } = require('../util/jwt')

const { replace_fields, replace_field } = require('../util/field')

const { passwd_encrypt_md5 } = require('../util/crypt')

const opClient = require('../util/redis')
const client = opClient.duplicate() // 需要使用这个

const { del_fields } = require('../util/field')
const { TOKEN_PREFIX, USER_LIST_SORT_SET } = require('../consts/system')

const { randStr } = require('../util/random')

const remove_fields = (obj, fields = ['passwd', 'salt', 'is_admin']) => {
  del_fields(obj, fields)
}

class SysUserService {
  async create(ctx) {
    try {
      const data = ctx.request.body
      // 手机号重复检查
      const user = await getByPhone(data.phone)
      if (user) {
        return ctx.app.emit('error', AccountExistsErr, ctx)
      }
      // 密码处理
      const salt = randStr(6)
      data.salt = salt

      data.passwd = passwd_encrypt_md5(data.passwd, salt)
      data.enable = true
      //
      const result = await create(data)
      // 处理返回字段
      replace_fields(result)
      remove_fields(result)

      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async update(ctx) {
    try {
      const data = ctx.request.body
      if (data.passwd && data.newPasswd) {
        // 判断原密码是否正确
        const user = await get(
          data.id,
          {
            deleted_at: null,
          },
          false // 不使用过滤
        )
        console.log(user, '<<<< user')
        if (!user) {
          return ctx.app.emit('error', AccountNotExistsErr, ctx)
        }
        if (passwd_encrypt_md5(data.passwd, user.salt) !== user.passwd) {
          return ctx.app.emit('error', AccountOrPasswordErr, ctx)
        }
        // 密码处理
        const salt = randStr(6)
        data.salt = salt
        data.passwd = passwd_encrypt_md5(data.passwd, salt)
      }

      const result = await update(data.id, data, {
        deleted_at: null,
      })

      // 处理返回字段
      replace_fields(result)

      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async get(ctx) {
    try {
      const user = ctx.state.user

      const result = await get(user.id, {
        deleted_at: null,
      })

      // 替换处理
      replace_fields(result)

      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async del(ctx) {
    try {
      const body = ctx.request.body
      const where = null

      // 先判断是否存在
      const usr = await this.get(body.id, {
        deleted_at: null,
      })
      if (!usr) {
        return ctx.app.emit('error', AccountNotExistsErr, ctx)
      }

      const result = await del(body.id, where)
      replace_fields(result)

      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async list(ctx) {
    try {
      const query = ctx.request.query
      const where = {
        deleted_at: null,
      }
      const data = await list(query.pageNo, query.pageSize, where) // 可以添加第三个参数条件过滤
      // 敏感字段处理
      data.map((item) => {
        replace_fields(item, [
          { field: 'name' },
          { field: 'phone', start: 3, end: -4 },
        ])
      })
      const total = await count(where)
      const result = {
        list: data,
        count: total,
      }
      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async login(ctx) {
    try {
      const data = ctx.request.body
      // 通过账号密码获取账号信息
      const result = await loginByPhone(data.phone)
      if (!result) {
        return ctx.app.emit('error', AccountOrPasswordErr, ctx)
      }
      if (!result.enable) {
        return ctx.app.emit('error', AccountDisabledErr, ctx)
      }

      // 校验密码
      if (result.passwd !== passwd_encrypt_md5(data.passwd, result.salt)) {
        return ctx.app.emit('error', AccountOrPasswordErr, ctx)
      }

      // 逻辑处理 生成token 并返回
      replace_fields(result)
      const name = result.name
      const phone = result.phone

      // TOOD 获取jwtToken
      const secret = process.env.JWT_SECRET
      const expiresIn = +process.env.JWT_EXPIRE_IN
      const options = {
        expiresIn: expiresIn,
      }
      // 生成token
      const token = createToken(
        {
          id: result.id,
          name: name,
          phone,
          role: result.is_admin ? 'admin' : 'normal',
        },
        secret,
        options
      )

      // redis cache
      const conn = await client.connect()
      // 缓存数据(可对登录token进行管理)
      await conn.set(`${TOKEN_PREFIX}:${phone}`, token, {
        // 使用 phone 即(xxx****xxxx) 与 判断token一致
        EX: expiresIn,
      })
      // 添加到列表中
      await conn.zAdd(USER_LIST_SORT_SET, {
        score: new Date().getTime(),
        value: phone, // 使用 phone 即(xxx****xxxx) 与 判断token一致
      })
      await conn.close()

      ctx.body = new Success({
        token,
        expires_in: expiresIn,
      }).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }

  async updatePwd(ctx) {
    try {
      const data = ctx.request.body

      const id = ctx.state.user.id
      const user = await get(id, { deleted_at: null }, false)

      // 判断原密码是否正确
      if (passwd_encrypt_md5(data.passwd, user.salt) !== user.passwd) {
        return ctx.app.emit('error', AccountOrPasswordErr, ctx)
      }
      const salt = randStr(6)
      data.salt = salt
      data.passwd = passwd_encrypt_md5(data.newPasswd, salt)
      // 删除 newPasswd
      remove_fields(data, ['newPasswd'])
      // 判断账号原密码是否正确 正确才做密码更新
      const result = await update(id, data)

      replace_fields(result)

      ctx.body = new Success(result).toString()
    } catch (err) {
      return ctx.app.emit('error', new SysErr(err.message, err.stack), ctx)
    }
  }
}
module.exports = new SysUserService()
